/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.core.execution; import java.beans.PropertyVetoException; import java.io.File; import java.io.FileFilter; import java.io.InterruptedIOException; import java.io.IOException; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Hashtable; import java.util.ResourceBundle; import java.util.ResourceBundle; import java.util.HashSet; import java.util.ArrayList; import java.util.Iterator; import java.security.SecureClassLoader; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.Policy; import java.security.Permission; import java.lang.ref.SoftReference; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.MalformedURLException; import org.openide.loaders.DataObject; import org.openide.TopManager; import org.openide.execution.NbClassPath; import org.openide.execution.ExecInfo; import org.openide.execution.ExecutorTask; import org.openide.execution.Executor; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; import org.openide.windows.InputOutput; import org.openide.modules.ManifestSection; /** Execution that provides support for starting a class with main * * @author Ales Novak * * The class handles redirecting of out/in/err for all tasks in the system. * First instance of TaskIO is created for Corona. So call System.out.println() * from Corona is redirected to a window. * Situation for executed task is following it uses System.out/err/in - because * the task is in some threadgroup it will be recognized and new panel in window * for that task will be created. Further System.out.println() means that * (in System.out is our class now - SysOut) calling thread is found and its threadgroup * is examined - SysOut propagates call to taskIOs in ExecutionEngine. IOTable * look for mapping the threadgroup to TaskIO class (it may create that). TaskIO * is created uninitialized. So if only out is used, err/in are never initialized. * Initializing is lazy - for request. TaskIO.out is an instance of SysPrintStream, * that is redirected to OutputWriter that is redirected to a window. */ public final class ExecutionEngine extends org.openide.execution.ExecutionEngine { /** base group for all running tasks */ public static final ThreadGroup base = new ThreadGroup("base"); // NOI18N /** used for naming groups */ private int number = 1; /** IO class for corona */ public static final TaskIO systemIO = new TaskIO(); /** maps ThreadGroups to TaskIO */ static IOTable taskIOs; static { taskIOs = new IOTable(base, systemIO); } /* table of window:threadgrp */ static private WindowTable wtable = new WindowTable(); /** this class have to force consistency of nodes displaying running processes * and actually running processes. This flag indicates consistency check. */ private boolean execNodeInited = false; /** list of ExecutionListeners * @associates ExecutionListener*/ private HashSet executionListeners = new HashSet(); /** instance of ExecutionEngine */ private static ExecutionEngine engineRef; static { systemIO.out = System.out; systemIO.err = System.err; systemIO.in = System.in; engineRef = new ExecutionEngine(); } static final long serialVersionUID =9072488605180080803L; private ExecutionEngine () { /* SysIn is a class that redirects System.in of some running task to a window (probably OutWindow). SysOut/Err are classes that redirect out/err to the window */ System.setIn (new SysIn ()); System.setOut (new SysOut ()); System.setErr (new SysErr ()); } /** */ public static ExecutionEngine getExecutionEngine() { return engineRef; } /** ExecutionObject is responsible for displaying runnig processes in a tree * so it must be created before first process is run */ private void initExecNode() { ProcessNode.getExecutionNode(); execNodeInited = true; try { if (org.openide.util.Utilities.isUnix()) { // init thread "process reaper" Class clz = Class.forName("java.lang.UNIXProcess"); // NOI18N } } catch (ClassNotFoundException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); // NOI18N } } } /** Should prepare environment for Executor and start it. Is called from * Executor.execute method. * * @param executor to start * @param info about class to start */ public ExecutorTask execute(String name, Runnable run, InputOutput inout) { if (! execNodeInited) initExecNode(); // initiate checking processes TaskThreadGroup g; if (name != null && name.indexOf("Applet") >= 0) { // NOI18N g = new AppletTaskGroup(base, "exec_" + name + "_" + number); // NOI18N } else { g = new TaskThreadGroup(base, "exec_" + name + "_" + number); // NOI18N } ExecutorTaskImpl task = new ExecutorTaskImpl(); synchronized (task.lock) { try { new RunClass (g, name, number++, inout, this, task, run); task.lock.wait(); } catch (InterruptedException e) { throw new IllegalStateException(e.getMessage()); } } return task; } /** Method that allows implementor of the execution engine to provide * class path to all libraries that one could find useful for development * in the system. * * @return class path to libraries */ protected NbClassPath createLibraryPath() { return new NbClassPath(getLibraries()); } /** Should get all jar and zip files that are used by IDE. * It should contain modules, and libraries. * @return an array of all jar and zip files installed for netbeans */ private final String[] getLibraries() { String fileSeparator = java.io.File.separator; String netbeansHome = System.getProperty("netbeans.home") + fileSeparator; // NOI18N String[][][] libs = new String[2][4][]; int len = 0; // nb home libs[0][0]= getLibraryItems(netbeansHome + "modules" + fileSeparator + "patches"); // NOI18N len += libs[0][0].length; libs[0][1] = getLibraryItems(netbeansHome + "lib", true); // NOI18N len += libs[0][1].length; libs[0][2] = getLibraryItems(netbeansHome + "modules"); // NOI18N len += libs[0][2].length; libs[0][3] = getLibraryItems(netbeansHome + "modules" + fileSeparator + "ext"); // NOI18N len += libs[0][3].length; // user home String userHome = System.getProperty("user.home"); // NOI18N if (userHome != null) { userHome = userHome + fileSeparator; libs[1][0]= getLibraryItems(netbeansHome + "modules" + fileSeparator + "patches"); // NOI18N len += libs[1][0].length; libs[1][1] = getLibraryItems(netbeansHome + "lib"); // NOI18N len += libs[1][1].length; libs[1][2] = getLibraryItems(netbeansHome + "modules"); // NOI18N len += libs[1][2].length; libs[1][3] = getLibraryItems(netbeansHome + "modules" + fileSeparator + "ext"); // NOI18N len += libs[1][3].length; } else { libs[1][0] = libs[1][1] = libs[1][2] = libs[1][3] = new String[0]; } String[] ret = new String[len]; int copiedlen = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 2; j++) { System.arraycopy(libs[j][i], 0, ret, copiedlen, libs[j][i].length); copiedlen += libs[j][i].length; } } return ret; } /** @return all zip and jar files from given directory */ private static String[] getLibraryItems(String lib) { return getLibraryItems(lib, false); } /** @return all zip and jar files from given directory */ private static String[] getLibraryItems(String lib, boolean checkClasspath) { final File file = new File(lib); final File[] files = file.listFiles(ZipFileFilter.instance()); if (files == null) { return new String [0]; } String[] ret = new String[files.length]; int skipped = 0; for (int i = 0; i < ret.length; i++) { String sfile = files[i].toString(); if (checkClasspath && (sfile.endsWith("developer.jar") || sfile.endsWith("openide.jar"))) { // NOI18N skipped++; continue; } ret[i] = files[i].toString(); } if (skipped > 0) { String[] rep = new String[ret.length - skipped]; System.arraycopy(ret, 0, rep, 0, rep.length); ret = rep; } return ret; } /** adds a listener */ public final void addExecutionListener (ExecutionListener l) { synchronized (executionListeners) { executionListeners.add(l); } } /** removes a listener */ public final void removeExecutionListener (ExecutionListener l) { synchronized (executionListeners) { executionListeners.remove(l); } } /** Creates new PermissionCollection for given CodeSource and given PermissionCollection. * @param cs a CodeSource * @param io an InputOutput * @return PermissionCollection for given CodeSource and InputOutput */ protected final PermissionCollection createPermissions(CodeSource cs, InputOutput io) { PermissionCollection pc = Policy.getPolicy().getPermissions(cs); ThreadGroup grp = Thread.currentThread().getThreadGroup(); return new IOPermissionCollection(io, pc, (grp instanceof TaskThreadGroup ? (TaskThreadGroup) grp: null)); } /** fires event that notifies about new process */ protected final void fireExecutionStarted (ExecutionEvent ev) { Iterator iter = ((HashSet) executionListeners.clone()).iterator(); while (iter.hasNext()) { ExecutionListener l = (ExecutionListener) iter.next(); l.startedExecution(ev); } } /** fires event that notifies about the end of a process */ protected final void fireExecutionFinished (ExecutionEvent ev) { Iterator iter = ((HashSet) executionListeners.clone()).iterator(); while (iter.hasNext()) { ExecutionListener l = (ExecutionListener) iter.next(); l.finishedExecution(ev); } } static void putWindow(java.awt.Window w, TaskThreadGroup tg) { wtable.putTaskWindow(w, tg); } static void closeGroup(ThreadGroup tg) { wtable.closeGroup(tg); } static boolean hasWindows(ThreadGroup tg) { return wtable.hasWindows(tg); } /** simple class for executing tasks in extra threads */ static final class RunClass extends Thread implements IOThreadIfc { /** InputOutput that is to be used */ private InputOutput io; /** name */ String allName; // used in innerclass Runnable /** reference to outer class */ private final org.netbeans.core.execution.ExecutionEngine engine; /** ref to a Task */ private final ExecutorTaskImpl task; /** Task to run */ private /*final*/ Runnable run; /** generated names */ static int number = 0; /** * @param base is a ThreadGroup we want to be in * @param m is a method to invoke * @param argv are params for the method */ public RunClass(TaskThreadGroup base, String name, int number, InputOutput io, org.netbeans.core.execution.ExecutionEngine engine, ExecutorTaskImpl task, Runnable run) { super (base, "exec_" + name + "_" + number); // NOI18N this.allName = name; this.io = io; this.engine = engine; this.task = task; this.run = run; this.start(); } /** runs the thread */ public void run() { final TaskThreadGroup mygroup = (TaskThreadGroup) getThreadGroup(); mygroup.setFinalizable(); // mark it finalizable - after the completetion of the current thread it will be finalized boolean fire = true; if (allName == null) { allName = generateName(); fire = false; } String ioname = java.text.MessageFormat.format( ProcessNode.getBundle().getString("CTL_ProgramIO"), new Object[] { allName } ); // prepare environment (threads, In/Out, atd.) DefaultSysProcess def; if (io != null) { def = new DefaultSysProcess(this, mygroup, io, allName); TaskIO tIO = new TaskIO(io, ioname, true); getTaskIOs ().put (io, tIO); } else { // advance TaskIO for this process TaskIO tIO = null; tIO = getTaskIOs().getTaskIO(ioname); if (tIO == null) { // executed for the first time io = TopManager.getDefault().getIO(ioname); tIO = new TaskIO(io, ioname); } else { io = tIO.getInout(); } io.select(); io.setFocusTaken(true); getTaskIOs().put(io, tIO); def = new DefaultSysProcess(this, mygroup, io, allName); } ExecutionEvent ev = null; try { ev = new ExecutionEvent(engine, def); if (fire) { engine.fireExecutionStarted(ev); } synchronized (task.lock) { task.proc = def; task.lock.notifyAll(); } // exec foreign Runnable run.run(); // throw away user runnable run = null; int result = 2; try { result = def.result(); } catch (ThreadDeath err) { // terminated while executing } task.result = result; } finally { if (ev != null) { if (fire) { engine.fireExecutionFinished(ev); } } closeGroup(mygroup); // free windows task.finished(); getTaskIOs().free(mygroup, io); // closes output } } // run method public InputOutput getInputOutput() { return io; } static String generateName() { return java.text.MessageFormat.format( ProcessNode.getBundle().getString("CTL_GeneratedName"), new Object[] {new Integer(number++)} ); } } /** * @return IOTable with couples ThreadGroup:TaskIO */ static IOTable getTaskIOs() { return taskIOs; } /** finds top thread group of the calling thread * @return null iff the calling thread is not in any exec group * or exec group of calling thread */ public static ThreadGroup findGroup () { ThreadGroup g = Thread.currentThread().getThreadGroup (); ThreadGroup old = null; while (g != null && g != base) { old = g; g = g.getParent (); } return (g == null) ? null : old; } /** jar and zip FileNameFiletr */ private static class ZipFileFilter implements FileFilter { /** an instance */ static SoftReference instance; public boolean accept(File fname) { String name = fname.toString(); return (name != null) && (name.endsWith(".jar") || name.endsWith(".zip")) && // NOI18N fname.isFile(); } static ZipFileFilter instance() { ZipFileFilter ret; if ((instance == null) || ((ret = (ZipFileFilter) instance.get()) == null)) { ret = new ZipFileFilter(); instance = new SoftReference(ret); return ret; } else { return ret; } } } } /* * Log * 43 Gandalf 1.42 1/28/00 Ales Novak UNIXProcess - "process * reaper" initialized * 42 Gandalf 1.41 1/14/00 Ales Novak System.err was not * redirected * 41 Gandalf 1.40 1/13/00 Ales Novak #4995 * 40 Gandalf 1.39 1/12/00 Ales Novak i18n * 39 Gandalf 1.38 1/11/00 Ales Novak * 38 Gandalf 1.37 1/11/00 Ales Novak provided InputOutput is * not handled by execution system * 37 Gandalf 1.36 12/15/99 Ales Novak making IO visible * 36 Gandalf 1.35 11/24/99 Ales Novak closing of OutputWindow * tabs made late * 35 Gandalf 1.34 11/17/99 Ales Novak #4438 * 34 Gandalf 1.33 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 33 Gandalf 1.32 10/8/99 Jaroslav Tulach Output tab works for * processes assigning own InputOutput * 32 Gandalf 1.31 10/8/99 Ales Novak #4374 * 31 Gandalf 1.30 10/8/99 Ales Novak IOException removed * 30 Gandalf 1.29 10/8/99 Ales Novak improved redirection of * IO operations * 29 Gandalf 1.28 10/4/99 Jaroslav Tulach SysProcess deleted. * 28 Gandalf 1.27 10/1/99 Ales Novak major change of * execution * 27 Gandalf 1.26 9/10/99 Jaroslav Tulach Services changes. * 26 Gandalf 1.25 8/9/99 Ian Formanek Generated Serial Version * UID * 25 Gandalf 1.24 6/28/99 Jaroslav Tulach Debugger types are like * Executors * 24 Gandalf 1.23 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 23 Gandalf 1.22 6/8/99 Ales Novak # 2096 * 22 Gandalf 1.21 5/31/99 Jaroslav Tulach External Execution & * Compilation * 21 Gandalf 1.20 5/27/99 Jaroslav Tulach Executors rearanged. * 20 Gandalf 1.19 5/25/99 Ales Novak proc item set * notification * 19 Gandalf 1.18 5/25/99 Ales Novak SysProcess set in * ExecutionTaskImpl * 18 Gandalf 1.17 5/6/99 Ales Novak not changed * 17 Gandalf 1.16 5/4/99 Jaroslav Tulach Correct processing of * executors. * 16 Gandalf 1.15 4/28/99 Ales Novak obsolete comment removed * 15 Gandalf 1.14 4/20/99 Ales Novak output was not reused * when exception occured * 14 Gandalf 1.13 4/16/99 Libor Martinek * 13 Gandalf 1.12 4/10/99 Ales Novak * 12 Gandalf 1.11 4/8/99 Ian Formanek Removed HotJava hack * 11 Gandalf 1.10 3/31/99 Ales Novak * 10 Gandalf 1.9 3/31/99 Ales Novak * 9 Gandalf 1.8 3/26/99 Ales Novak * 8 Gandalf 1.7 3/24/99 Ales Novak * 7 Gandalf 1.6 3/19/99 Ales Novak * 6 Gandalf 1.5 2/11/99 Ian Formanek Renamed FileSystemPool * -> Repository * 5 Gandalf 1.4 1/15/99 Ales Novak * 4 Gandalf 1.3 1/12/99 Jaroslav Tulach Temporary disabled * redirection of I/O * 3 Gandalf 1.2 1/8/99 Ales Novak * 2 Gandalf 1.1 1/6/99 Ian Formanek Reflecting change in * datasystem package * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ * Beta Change History: * 0 Tuborg 0.11 --/--/98 Ales Novak redesign of execution * 0 Tuborg 0.12 --/--/98 Petr Hamernik dataobject deleted. */